<template>
  <!-- #ifndef APP-NVUE -->
  <view
    :class="['uni-col', sizeClass, pointClassList]"
    :style="{
      paddingLeft: `${Number(gutter)}rpx`,
      paddingRight: `${Number(gutter)}rpx`,
    }"
  >
    <slot></slot>
  </view>
  <!-- #endif -->
  <!-- #ifdef APP-NVUE -->
  <!-- 在nvue上，类名样式不生效，换为style -->
  <!-- 设置right正值失效，设置 left 负值 -->
  <view
    :class="['uni-col']"
    :style="{
      paddingLeft: `${Number(gutter)}rpx`,
      paddingRight: `${Number(gutter)}rpx`,
      width: `${nvueWidth}rpx`,
      position: 'relative',
      marginLeft: `${marginLeft}rpx`,
      left: `${right === 0 ? left : -right}rpx`,
    }"
  >
    <slot></slot>
  </view>
  <!-- #endif -->
</template>

<script>
  /**
   * Col	布局-列
   * @description	搭配uni-row使用，构建布局。
   * @tutorial	https://ext.dcloud.net.cn/plugin?id=3958
   *
   * @property	{span} type = Number 栅格占据的列数
   * 						默认 24
   * @property	{offset} type = Number 栅格左侧的间隔格数
   * @property	{push} type = Number 栅格向右移动格数
   * @property	{pull} type = Number 栅格向左移动格数
   * @property	{xs} type = [Number, Object] <768px 响应式栅格数或者栅格属性对象
   * 						@description	Number时表示在此屏幕宽度下，栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
   * @property	{sm} type = [Number, Object] ≥768px 响应式栅格数或者栅格属性对象
   * 						@description	Number时表示在此屏幕宽度下，栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
   * @property	{md} type = [Number, Object] ≥992px 响应式栅格数或者栅格属性对象
   * 						@description	Number时表示在此屏幕宽度下，栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
   * @property	{lg} type = [Number, Object] ≥1200px 响应式栅格数或者栅格属性对象
   * 						@description	Number时表示在此屏幕宽度下，栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
   * @property	{xl} type = [Number, Object] ≥1920px 响应式栅格数或者栅格属性对象
   * 						@description	Number时表示在此屏幕宽度下，栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
   */
  const ComponentClass = "uni-col";

  // -1 默认值，因为在微信小程序端只给Number会有默认值0
  export default {
    name: "uniCol",
    // #ifdef MP-WEIXIN
    options: {
      virtualHost: true, // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现
    },
    // #endif
    props: {
      span: {
        type: Number,
        default: 24,
      },
      offset: {
        type: Number,
        default: -1,
      },
      pull: {
        type: Number,
        default: -1,
      },
      push: {
        type: Number,
        default: -1,
      },
      xs: [Number, Object],
      sm: [Number, Object],
      md: [Number, Object],
      lg: [Number, Object],
      xl: [Number, Object],
    },
    data() {
      return {
        gutter: 0,
        sizeClass: "",
        parentWidth: 0,
        nvueWidth: 0,
        marginLeft: 0,
        right: 0,
        left: 0,
      };
    },
    created() {
      // 字节小程序中，在computed中读取$parent为undefined
      let parent = this.$parent;

      while (parent && parent.$options.componentName !== "uniRow") {
        parent = parent.$parent;
      }

      this.updateGutter(parent.gutter);
      parent.$watch("gutter", (gutter) => {
        this.updateGutter(gutter);
      });

      // #ifdef APP-NVUE
      this.updateNvueWidth(parent.width);
      parent.$watch("width", (width) => {
        this.updateNvueWidth(width);
      });
      // #endif
    },
    computed: {
      sizeList() {
        let { span, offset, pull, push } = this;

        return {
          span,
          offset,
          pull,
          push,
        };
      },
      // #ifndef APP-NVUE
      pointClassList() {
        let classList = [];

        ["xs", "sm", "md", "lg", "xl"].forEach((point) => {
          const props = this[point];
          if (typeof props === "number") {
            classList.push(`${ComponentClass}-${point}-${props}`);
          } else if (typeof props === "object" && props) {
            Object.keys(props).forEach((pointProp) => {
              classList.push(
                pointProp === "span"
                  ? `${ComponentClass}-${point}-${props[pointProp]}`
                  : `${ComponentClass}-${point}-${pointProp}-${props[pointProp]}`
              );
            });
          }
        });

        // 支付宝小程序使用 :class=[ ['a','b'] ]，渲染错误
        return classList.join(" ");
      },
      // #endif
    },
    methods: {
      updateGutter(parentGutter) {
        parentGutter = Number(parentGutter);
        if (!isNaN(parentGutter)) {
          this.gutter = parentGutter / 2;
        }
      },
      // #ifdef APP-NVUE
      updateNvueWidth(width) {
        // 用于在nvue端，span，offset，pull，push的计算
        this.parentWidth = width;
        ["span", "offset", "pull", "push"].forEach((size) => {
          const curSize = this[size];
          if ((curSize || curSize === 0) && curSize !== -1) {
            let RPX = (1 / 24) * curSize * width;
            RPX = Number(RPX);
            switch (size) {
              case "span":
                this.nvueWidth = RPX;
                break;
              case "offset":
                this.marginLeft = RPX;
                break;
              case "pull":
                this.right = RPX;
                break;
              case "push":
                this.left = RPX;
                break;
            }
          }
        });
      },
      // #endif
    },
    watch: {
      sizeList: {
        immediate: true,
        handler(newVal) {
          // #ifndef APP-NVUE
          let classList = [];
          for (let size in newVal) {
            const curSize = newVal[size];
            if ((curSize || curSize === 0) && curSize !== -1) {
              classList.push(
                size === "span"
                  ? `${ComponentClass}-${curSize}`
                  : `${ComponentClass}-${size}-${curSize}`
              );
            }
          }
          // 支付宝小程序使用 :class=[ ['a','b'] ]，渲染错误
          this.sizeClass = classList.join(" ");
          // #endif
          // #ifdef APP-NVUE
          this.updateNvueWidth(this.parentWidth);
          // #endif
        },
      },
    },
  };
</script>

<style lang="scss" scoped>
  /* breakpoints */
  $--sm: 768px !default;
  $--md: 992px !default;
  $--lg: 1200px !default;
  $--xl: 1920px !default;

  $breakpoints: (
    "xs": (
      max-width: $--sm - 1,
    ),
    "sm": (
      min-width: $--sm,
    ),
    "md": (
      min-width: $--md,
    ),
    "lg": (
      min-width: $--lg,
    ),
    "xl": (
      min-width: $--xl,
    ),
  );

  $layout-namespace: ".uni-";
  $col: $layout-namespace + "col";

  @function getSize($size) {
    /* TODO 1/24 * $size * 100 * 1%; 使用计算后的值，为了解决 vue3 控制台报错 */
    @return 0.04166666666 * $size * 100 * 1%;
  }

  @mixin res($key, $map: $breakpoints) {
    @if map-has-key($map, $key) {
      @media screen and #{inspect(map-get($map,$key))} {
        @content;
      }
    } @else {
      @warn "Undeinfed point: `#{$key}`";
    }
  }

  /* #ifndef APP-NVUE */
  #{$col} {
    float: left;
    box-sizing: border-box;
  }

  #{$col}-0 {
    /* #ifdef APP-NVUE */
    width: 0;
    height: 0;
    margin-top: 0;
    margin-right: 0;
    margin-bottom: 0;
    margin-left: 0;
    /* #endif */
    /* #ifndef APP-NVUE */
    display: none;
    /* #endif */
  }

  @for $i from 0 through 24 {
    #{$col}-#{$i} {
      width: getSize($i);
    }

    #{$col}-offset-#{$i} {
      margin-left: getSize($i);
    }

    #{$col}-pull-#{$i} {
      position: relative;
      right: getSize($i);
    }

    #{$col}-push-#{$i} {
      position: relative;
      left: getSize($i);
    }
  }

  @each $point in map-keys($breakpoints) {
    @include res($point) {
      #{$col}-#{$point}-0 {
        display: none;
      }

      @for $i from 0 through 24 {
        #{$col}-#{$point}-#{$i} {
          width: getSize($i);
        }

        #{$col}-#{$point}-offset-#{$i} {
          margin-left: getSize($i);
        }

        #{$col}-#{$point}-pull-#{$i} {
          position: relative;
          right: getSize($i);
        }

        #{$col}-#{$point}-push-#{$i} {
          position: relative;
          left: getSize($i);
        }
      }
    }
  }

  /* #endif */
</style>
